
/*-------------------------------------------------------*/
/*                                                       */
/* [c]copyright 1996 by AlphaHelix                       */
/*                                                       */
/* This is the mainmodule of RAVAGE. And I wrote it.     */
/* Yes. I wrote it on my own.                            */
/*                                                       */
/*-------------------------------------------------------*/

/*
 Ultimate DEBUG MASTER switches.
 These switches must be set for EACH and EVERY module in the project.

 DEBUG   : Enables extended runtime checks.
           - Memory check (out of memory, unfreed memory, messed up
             memory structure, trying to free unallocated memory).
           - Parameters in range check.
           - In play DEBUG keys are enabled.
             (a = bypass frame limiter)
             (s = enter\leave single step mode)
             (. = do single step)
             (d = execute INT 3 exeption ----> ENTER DEBUGGER)
             (i = toggle invincible on/off.)

 RECORDER: Enables access to the game recorder for DEMO recording.
 SHAREWARE:Build SHAREWARE VERSION of RAVAGE.
*/

#include <malloc.h>
#include <setjmp.h>
#include <sys\types.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream.h>
#include <i86.h>
#include <dos.h>

#define AH_MAINMODULE
#include "error.hpp"
#include "memory.hpp"
#include "types.h"
#include "vtimer.h"     // virtual timer module.
#include "timer.hpp"    // easy timer module.
#include "xlib.hpp"
#include "objects.hpp"
#include "input.hpp"
#include "diskio.hpp"
#include "ravage.hpp"
#include "sound.hpp"


// Some variables.
#ifdef SHAREWARE
const char copyright[] = "RAVAGE shareware 1.1 [c]copyright 1996 by AlphaHelix.";
#else
const char copyright[] = "RAVAGE registered 1.1 [c]copyright 1996 by AlphaHelix.";
#endif

static int      windows;          // 1 if Windows is running.
static int      easy_timer;       // Flag indicating easy timer on or off.
static int      s_timer;          // Sound system timer.


/*---------------------------------------------------------
 Function: timer

 Description:
 This is the all important timer function.
 This function will be called 60 times a second and
 updates the "timer_ticks" variable.
 "timer_ticks" is then used in the rest of the program
 to synchronize game speed and screen output to 30
 frames per second.
---------------------------------------------------------*/
volatile int  timer_ticks;
void timer(void)
{
   timer_ticks++;
}



/*---------------------------------------------------------
 Function:

 Description:
 Critical error handler for DOS int 24h ?
---------------------------------------------------------*/
#pragma off (unreferenced)
int __far critical_err(unsigned dev, unsigned err, unsigned __far *hdr)
{
   return _HARDERR_FAIL;
}
#pragma on (unreferenced)


/*---------------------------------------------------------
 Function: cmdline

 Description:
 Concatenate all command arguments to one string.
 So easy search can be done now.
---------------------------------------------------------*/
#define CMDLEN       40             // Max allowed length of command line.
static char    cmd[CMDLEN];         // Command line arguments.
int incmd(char *text)
{
   return (int)strstr(cmd, text);
}

static int cmdnumber(char *text, int def)
{
   char  *i = strstr(cmd, text);

   if (!i) return def;
   return i[strlen(text)] - '0';
}

static void cmdline(int argc, char *argv[])
{
   int   i;

// Concatenate all command strings together in 'cmd'.
   cmd[0] = '\0';			// Clear string.
   for (i = 1; i < argc; i++) {
      if (strlen(cmd)+strlen(argv[i]) < CMDLEN)
	 strcat(cmd, argv[i]);
   }
   strlwr(cmd);

}



/*---------------------------------------------------------
 Function:

 Description:
 Check for Microsoft Windows running.
 Return !=0 if windows is running.
---------------------------------------------------------*/
int wincheck(void)
{
   union REGS r;

   r.x.eax = 0x1600;       // Windows installation check.
   int386(0x2f, &r, &r);

   return r.x.eax & 0xff;
}

/*---------------------------------------------------------
 Function: loadsettings & savesettings

 Description:
 These functions are used to save\load the configuration
 the user has choosen.
---------------------------------------------------------*/
static void loadsettings(char *file)
{
   myFile   myfile;

// Fill in settings structure with FACTORY DEFAULTS.

// Sound settings.
   settings.voices = 2;
   settings.sound_device = 1;
   settings.mix_freq = 44100;
   settings.mod_vol = 10;
   settings.wav_vol = 30;

// Control settings.
// mouse.
   settings.msens = M_MEDIUM;
// keyboard
   settings.key[0] = 0x4b;       // <-
   settings.key[1] = 0x4d;       // ->
   settings.key[2] = 0x48;       // /\
   settings.key[3] = 0x50;       // \/
   settings.key[4] = 0x39;       // BUMM.

   settings.device[0] = 1;       // Player 1 input: Keyboard.
   settings.device[1] = 3;       // Player 2 input: Mouse.

// Graphix
   settings.frate = 1;
   settings.shadows = 2;
   settings.cpu_usage = 0;

// Try to load configuration file.
   if (myfile.open(file)) {
      myfile.read(&settings, sizeof(Settings));
      myfile.close();
   }
}

static void savesettings(char *file)
{
   myFile   myfile;

   if (!myfile.creat(file)) return;
   myfile.write(&settings, sizeof(Settings));
   myfile.close();

}

/*---------------------------------------------------------
 Function: powerdown

 Description:
 Function called upon exit of the program.
 Does all the needed housekeeping AFTER the
 registered exit functions (reg_exit) have been
 called.
---------------------------------------------------------*/
static void powerdown(void)
{
#ifdef DEBUG
   memory_usage();
#endif
   cout << "Object peak usage: " << object_usage() << " of "
        << OBJ_MAXOBJECTS << " used." << endl;
   cout << "Weapon peak usage: " << weapon_usage() << " of "
        << G_MAXWEAPONS << " used." << endl;
   cout << "Enemy peak usage: " << enemy_usage() << " of "
        << G_MAXENEMIES << " used." << endl;

   _heapmin();

   cout << endl;
   cout << "Thank you for playing RAVAGE." << endl << endl;
}

/*---------------------------------------------------------
 Function: powerup

 Description:
 Initialize the system.
 - memory manager
 - timer
 - sound card
 - graphics
 - inputs (keyboard, mouse, joystick)
---------------------------------------------------------*/
static void powerup(void)
{
// Install DOS error handler.
   _harderr(critical_err);

// Check for running windows.
   if (windows = wincheck()) {
      cout << "Windows is running." << endl;
      cout << "We suggest you quit Windows and run RAVAGE in DOS mode." << endl;
      cout << "Performance is better in DOS mode however there's no" << endl;
      cout << "reason why it will not otherwise work in WIN mode." << endl;
      cout << endl;
   }

   easy_timer = windows || incmd("/et");
   if (easy_timer) cout << "Easy Timer installed." << endl << endl;

   atexit(powerdown);

// Grow heap.
   {
      char *ptr;
      ptr = new char[MEMNEEDED*1024];
      if (ptr == 0) error("Not enough memory available.");
      delete []ptr;
   }

// Load configuration.
   loadsettings(CONFIGFILE);

// Init File Sytem.
   initfilesystem(); reg_exit(shutfilesystem);
// Initialize Inputs.
   if (incmd("/j")) {
      initinputs(INPUT_KEYBOARD | INPUT_MOUSE);
   } else {
      initinputs(INPUT_KEYBOARD | INPUT_MOUSE | INPUT_JOYPAD);
   }
   reg_exit(shutinputs);
   if (!lockdevice(settings.device[0])) {
      settings.device[0] = nextdevice(0);
      lockdevice(settings.device[0]);
   }
   if (!lockdevice(settings.device[1])) {
      settings.device[1] = nextdevice(0);
      lockdevice(settings.device[1]);
   }
// Define keys according to loaded settings.
   setkeys(settings.key[0], settings.key[1],
           settings.key[2], settings.key[3], settings.key[4]);
   if (joypad) cout << "Joypad detected." << endl;
   if (mouse) cout << "Mouse detected." << endl;

// Init Sound system.
   initsound(settings.sound_device, settings.mix_freq); reg_exit(shutsound);
   s_setvoices(settings.voices);
   s_setmodvol(settings.mod_vol);
   s_setwavvol(settings.wav_vol);
   cout << "Sound driver: " << s_drivername() << endl;

// Wait for a boring key being pressed.
   cout << endl;
   cout << "Press any key." << endl;
   input_fire();

// Init OBJECT system.
   initobjects(); reg_exit(shutobjects);
   shadow_level(settings.shadows);
// Init game classes.
   initgclass(); reg_exit(shutgclass);
// Enter graphics.
   initgraphics(); reg_exit(shutgraphics);

// Start TIMERS.
   if (easy_timer) {
// Easy timer. (ET = Easy Timer)
      ET_init(60); reg_exit(ET_shut);
      ET_setmode(0);
      ET_reg(timer);    // Register screen timer.
   } else {
// Virtual timer (VT = Virtual Timer)
      VT_Init(); reg_exit(VT_Exit);
      VT_Mode(VT_VRSYNC);
      VT_V0Handler(timer);
   }

// Measure CPU Power.
   cpu_power = 0;
// Synchronize to timer.
   timer_ticks = 0; while (timer_ticks < 100);
// DO MEASURE.
   timer_ticks = 0;
   while (timer_ticks < 2) cpu_power++;
   cpu_power /= 100;

// Do final settings on timer.
// Start to call old timer.
   if (easy_timer) ET_setmode(ET_CALLOLD); else VT_Mode(VT_VRSYNC | VT_CALLOLD);

// Start SOUND update timer.
   if (easy_timer) {
      ET_reg(s_update);
   } else {
      s_timer = VT_Alloc();
      VT_SetHandler(s_timer, s_update);
      VT_SetBPM(s_timer, 125);
      VT_Start(s_timer);
   }

// No game is in progress.
   gstate.ready = 0;

// Process command line.
// First (DJ)Ammann cheats.
   cheatlevel = 0;
   if (incmd("/at")) cheatlevel |= CHEAT_INVUL;   // absolut toedlich.
   if (incmd("/vv")) cheatlevel |= CHEAT_MONEY;   // vertammi vertori.
// Level jumping.
   start_stage = cmdnumber("/l", 1);
   if (start_stage > MAXLEVELS) error("/l parameter out of range.");

#ifdef DEBUG
   if (incmd("/motz")) start_stage = 99;
#endif

#ifdef RECORDER
   recorder = 0;
   if (incmd("/rec")) recorder = 1;
#endif
}


void main(int argc, char *argv[])
{
   int   request;          // Returned request from MAIN MENU.

// Set up error handling LONG JUMP.
   if (setjmp(_err_jmp)) {
// If an error occurs we will jump here.
      reg_down();          // Properly shut down the system.
      post_error();        // Post error processing.
      exit(1);
   }

// Display some message.
   cout << endl << copyright << endl << endl;
   cout << "usage: RAVAGE [options]" << endl;
   cout << "       /et     Easy Timer. Use this switch to" << endl;
   cout << "               solve your screen update problem." << endl;
   cout << "       /j      Don't use a joypad. Use this switch" << endl;
   cout << "               if you have joypad or joystick troubles." << endl;
   cout << endl;

// Prepare to access the command line.
   cmdline(argc, argv);

// Start the system.
   powerup();

// Open FilePool.
   filepool[0].open("RAV0.DAT");
   filepool[1].open("RAV1.DAT");
   filepool[2].open("RAV2.DAT");
   filepool[3].open("RAV3.DAT");

// Show TITLE screen.
   title();

// Play loop.
#ifdef DEBUG
   if (incmd("/shop")) {
      request = menu(SHOPMENU);
   } else {
#endif
   request = M_NONE;
   do {
      switch (request) {
      case M_PLAY:
// Enable RECORDER if needed.
         #ifdef RECORDER
            if (recorder) rec_enable();
         #endif
         play(0);    // Play NEW game.
         break;
      case M_LOAD:
         play(1);    // Load game.
         break;
      case M_SOUND:  // Reinitialize Sound System.
         shutsound();
         initsound(settings.sound_device, settings.mix_freq);
         s_setvoices(settings.voices);
         s_setmodvol(settings.mod_vol);
         s_setwavvol(settings.wav_vol);
         break;
      }
      if (request == M_SOUND) {
         request = menu(SOUNDMENU);
      } else {
         request = menu(MAINMENU);
      }
   } while (request != M_QUIT);
#ifdef DEBUG
   }
#endif

// Close FilePool.
   filepool[3].close();
   filepool[2].close();
   filepool[1].close();
   filepool[0].close();

// Done for the game. Back to OS.
   reg_down();                // Execute registered shutdown functions.
   savesettings(CONFIGFILE);  // Save current game settings.

   exit(0);                   // Exit nicely.
}

